package util

import (
    "encoding/binary"
    "errors"
    "fmt"
    "github.com/cloudwego/hertz/pkg/common/hlog"
)

// 气象传感器 - 铝壳风向传感器（485型，ZTS-3000-FXA-N01）

// WindDirectionSensorData 解析后的风向传感器数据
type WindDirectionSensorData struct {
    DeviceAddr    byte    // 设备地址（1-254，默认0x01）
    FuncCode      byte    // 功能码（读数据：0x03；写参数：0x06）
    ValidBytes    byte    // 应答帧有效数据字节数
    SensorType    string  // 传感器类型（8方位/16方位/360°）
    Level8        uint16  // 8方位档位（0-7档，仅8方位型有效）
    Angle8        uint16  // 8方位角度（0-315°，仅8方位型有效）
    Angle360Full  float64 // 360°完整角度（0-359.9°，8/16/360°型均有效）
    Level16       uint16  // 16方位档位（0-15档，仅16方位型有效）
    Angle360Int   uint16  // 360°整数角度（0-359°，仅360°型有效）
    BaudRate      uint16  // 设备波特率（对应值：0=2400,1=4800<默认>,2=9600...7=1200）
    NewDeviceAddr uint16  // 新设备地址（仅修改地址时使用，1-254）
    DirectionName string  // 风向名称（如“北风”“东风”，自动匹配档位/角度）
}

// BuildWindDirRequestFrame 构建风向传感器请求帧（支持读风向、改地址、改波特率）
// deviceAddr: 设备地址（默认0x01）
// funcCode: 功能码（读数据：0x03；写参数：0x06）
// startAddr: 起始寄存器地址（读风向：0x0000；地址配置：0x07D0；波特率配置：0x07D1）
// dataLength: 读取寄存器数量/写入参数值（读风向：0x0002<读2个寄存器>；写参数：新地址/波特率对应值）
func BuildWindDirRequestFrame(deviceAddr, funcCode byte, startAddr, dataLength uint16) ([]byte, error) {
    // 基础参数合法性校验（基于手册定义）
    if deviceAddr < 0x01 || deviceAddr > 0xFE {
        return nil, errors.New("设备地址非法，需在1-254范围内")
    }
    if funcCode != 0x03 && funcCode != 0x06 {
        return nil, errors.New("仅支持功能码0x03（读寄存器）和0x06（写寄存器）")
    }
    // 校验寄存器地址（仅支持手册中定义的关键寄存器）
    validStartAddrs := map[uint16]bool{0x0000: true, 0x07D0: true, 0x07D1: true}
    if !validStartAddrs[startAddr] {
        return nil, fmt.Errorf("起始寄存器地址非法，仅支持0x0000(风向数据)、0x07D0(设备地址)、0x07D1(波特率)")
    }
    // 读风向时固定读取2个寄存器（覆盖不同类型传感器的核心数据）
    if funcCode == 0x03 && startAddr == 0x0000 && dataLength != 0x0002 {
        return nil, errors.New("读风向数据时，寄存器长度需设为0x0002（读取2个核心寄存器）")
    }

    // 打印请求帧日志
    hlog.Debugf("构建风向传感器请求帧: 设备地址=0x%02X, 功能码=0x%02X, 起始寄存器=0x%04X, 数据长度/值=0x%04X",
        deviceAddr, funcCode, startAddr, dataLength)

    // 构建帧主体（6字节：地址码+功能码+起始地址(2字节)+数据长度/值(2字节)）
    frame := make([]byte, 6)
    frame[0] = deviceAddr
    frame[1] = funcCode
    binary.BigEndian.PutUint16(frame[2:4], startAddr)  // 寄存器地址高位在前
    binary.BigEndian.PutUint16(frame[4:6], dataLength) // 读：寄存器数量；写：参数值

    // 计算并添加CRC16校验码（遵循手册“低位在前、高位在后”规则）
    crc := calculateWindDirCRC(frame)
    frame = append(frame, byte(crc&0xFF))    // CRC低字节
    frame = append(frame, byte(crc>>8&0xFF)) // CRC高字节

    return frame, nil
}

// ParseWindDirResponseFrame 解析风向传感器应答帧（自动适配8/16/360°型传感器）
// response: 传感器返回的原始应答字节流
func ParseWindDirResponseFrame(response []byte) (*WindDirectionSensorData, error) {
    // 1. 基础长度校验（读风向应答9字节，写参数应答8字节，最小长度8字节）
    if len(response) < 8 {
        return nil, errors.New("应答帧长度不足，最小需8字节")
    }

    // 2. 提取帧头核心信息
    deviceAddr := response[0]
    funcCode := response[1]

    // 3. 设备错误响应检查（功能码最高位为1表示错误）
    if (funcCode & 0x80) != 0 {
        return nil, fmt.Errorf("设备返回错误，错误功能码=0x%02X", funcCode)
    }

    // 4. CRC校验（校验范围：除最后2字节CRC外的所有数据）
    dataPart := response[:len(response)-2]
    receivedCRC := binary.LittleEndian.Uint16(response[len(response)-2:]) // 接收的CRC（低位在前）
    calculatedCRC := calculateWindDirCRC(dataPart)
    if receivedCRC != calculatedCRC {
        return nil, fmt.Errorf("CRC校验失败，接收CRC=0x%04X，计算CRC=0x%04X", receivedCRC, calculatedCRC)
    }
    // 低位 0xDB 高位 0xC8
    if response[len(response)-2] != 0xDB || response[len(response)-1] != 0xC8 {
        return nil, fmt.Errorf("校验码错误，期望0xC8DB，实际0x%02X%02X", response[len(response)-2], response[len(response)-1])
    }
    result := &WindDirectionSensorData{
        DeviceAddr: deviceAddr,
        FuncCode:   funcCode,
        ValidBytes: response[2],
    }

    // 有效字节数校验（读2个寄存器需返回4字节数据）
    if result.ValidBytes != 0x04 {
        return nil, fmt.Errorf("读风向应答有效字节数异常，期望0x04，实际0x%02X", result.ValidBytes)
    }

    // 提取2个核心寄存器数据（0000H和0001H）
    reg0Data := binary.BigEndian.Uint16(response[3:5]) // 0000H寄存器数据
    reg1Data := binary.BigEndian.Uint16(response[5:7]) // 0001H寄存器数据

    // 自动识别传感器类型并解析数据（基于手册寄存器定义）
    switch {
    // 8方位型：reg0=0-7档，reg1=0-315°，额外通过0002H寄存器获取359.9°（此处简化用reg1推导）
    case reg0Data <= 7 && reg1Data <= 315 && reg1Data%45 == 0:
        result.SensorType = "8方位"
        result.Level8 = reg0Data
        result.Angle8 = reg1Data
        result.Angle360Full = float64(reg1Data) // 8方位角度对应固定值（0/45/90...315°）
        result.DirectionName = get8DirName(reg0Data)
        hlog.Debugf("8方位传感器解析结果：档位=%d，角度=%.1f°，风向=%s",
            result.Level8, result.Angle360Full, result.DirectionName)

    // 16方位型：reg0=0-359.9°，reg1=0-15档
    case reg0Data <= 3599 && reg1Data <= 15:
        result.SensorType = "16方位"
        result.Angle360Full = float64(reg0Data) / 10 // 0000H数据为角度×10（如3599→359.9°）
        result.Level16 = reg1Data
        result.DirectionName = get16DirName(reg1Data)
        hlog.Debugf("16方位传感器解析结果：档位=%d，角度=%.1f°，风向=%s",
            result.Level16, result.Angle360Full, result.DirectionName)

    // 360°型：reg0=0-3599（角度×10），reg1=0-359°（整数角度）
    case reg0Data <= 3599 && reg1Data <= 359:
        result.SensorType = "360°"
        result.Angle360Full = float64(reg0Data) / 10 // 还原为保留1位小数的角度
        result.Angle360Int = reg1Data
        result.DirectionName = get360DirName(result.Angle360Full)
        hlog.Debugf("360°传感器解析结果：整数角度=%d°，完整角度=%.1f°，风向=%s",
            result.Angle360Int, result.Angle360Full, result.DirectionName)

    default:
        return nil, fmt.Errorf("无法识别传感器类型，寄存器数据：0000H=0x%04X，0001H=0x%04X", reg0Data, reg1Data)
    }

    return result, nil
}

// calculateWindDirCRC 计算ModBus-RTU CRC16校验码（多项式0xA001，符合手册错误校验规则）
func calculateWindDirCRC(data []byte) uint16 {
    crc := uint16(0xFFFF) // 初始值
    for _, b := range data {
        crc ^= uint16(b)
        for i := 0; i < 8; i++ {
            if (crc & 0x0001) != 0 { // 最低位为1时，右移后异或多项式
                crc >>= 1
                crc ^= 0xA001
            } else {
                crc >>= 1
            }
        }
    }
    return crc
}

// get8DirName 根据8方位档位获取风向名称（对应手册8方位换算表）
func get8DirName(level uint16) string {
    dirMap := map[uint16]string{
        0: "北风", 1: "东北风", 2: "东风", 3: "东南风",
        4: "南风", 5: "西南风", 6: "西风", 7: "西北风",
    }
    return dirMap[level]
}

// get16DirName 根据16方位档位获取风向名称（对应手册16方位换算表）
func get16DirName(level uint16) string {
    dirMap := map[uint16]string{
        0: "北风", 1: "北东北风", 2: "东北风", 3: "东东北风",
        4: "东风", 5: "东东南风", 6: "东南风", 7: "南东南风",
        8: "南风", 9: "南西南风", 10: "西南风", 11: "西西南风",
        12: "西风", 13: "西西北风", 14: "西北风", 15: "北西北风",
    }
    return dirMap[level]
}

// get360DirName 根据360°角度获取风向名称（适配8方位逻辑，角度对应档位）
func get360DirName(angle float64) string {
    switch {
    case angle >= 348.75 || angle < 11.25:
        return "北风"
    case angle >= 11.25 && angle < 33.75:
        return "北东北风"
    case angle >= 33.75 && angle < 56.25:
        return "东北风"
    case angle >= 56.25 && angle < 78.75:
        return "东东北风"
    case angle >= 78.75 && angle < 101.25:
        return "东风"
    case angle >= 101.25 && angle < 123.75:
        return "东东南风"
    case angle >= 123.75 && angle < 146.25:
        return "东南风"
    case angle >= 146.25 && angle < 168.75:
        return "南东南风"
    case angle >= 168.75 && angle < 191.25:
        return "南风"
    case angle >= 191.25 && angle < 213.75:
        return "南西南风"
    case angle >= 213.75 && angle < 236.25:
        return "西南风"
    case angle >= 236.25 && angle < 258.75:
        return "西西南风"
    case angle >= 258.75 && angle < 281.25:
        return "西风"
    case angle >= 281.25 && angle < 303.75:
        return "西西北风"
    case angle >= 303.75 && angle < 326.25:
        return "西北风"
    case angle >= 326.25 && angle < 348.75:
        return "北西北风"
    default:
        return "未知风向"
    }
}

// getBaudRateVal 将波特率对应值转换为实际波特率（对应手册07D1H寄存器定义）
func getBaudRateVal(val uint16) uint32 {
    baudMap := map[uint16]uint32{
        0: 2400, 1: 4800, 2: 9600, 3: 19200,
        4: 38400, 5: 57600, 6: 115200, 7: 1200,
    }
    return baudMap[val]
}
